package com.fowo.api.common.util;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.*;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.JSONPath;
import com.fowo.api.common.messages.MessageKeys;
import com.fowo.api.common.messages.MessageUtils;
import com.fowo.api.common.model.ImportRow;
import com.fowo.api.common.model.JoinModel;
import com.fowo.api.common.model.JoinResult;
import com.fowo.api.sys.component.ExecuteScriptEngine;
import com.fowo.api.user.model.JwtUserInfo;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.net.InetAddress;
import java.time.format.DateTimeParseException;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 提供 Excel 工作表数据转换单元
 */
@UtilityClass
public class SheetUtils {

    /**
     * 返回给定的对象的所有实例字段是否都是 null 或空白字符串
     *
     * @param obj
     */
    public boolean isAllFieldsEmpty(Object obj) {
        if (obj == null) {
            return true;
        }
        final Class<?> clz = obj.getClass();
        final boolean[] results = new boolean[]{true};
        ReflectionUtils.doWithFields(clz, f -> {
            if (!results[0]) {
                // 已有结果，不用再处理
                return;
            }
            f.trySetAccessible();
            Object val = f.get(obj);
            if (val != null) {
                if (val instanceof String && !StringUtils.hasText(trim((String) val))) {
                    return;
                }
                results[0] = false;
            }
        }, f -> !Modifier.isStatic(f.getModifiers()));

        return results[0];
    }

    /**
     * 返回对象的字符串格式或 null
     *
     * @param obj 对象
     */
    public String toString(Object obj) {
        if (obj == null) {
            return null;
        }
        if (obj instanceof String) {
            return trim((String) obj);
        }
        return obj.toString();
    }

    /**
     * 返回字符串是否为 null 或空白
     *
     * @param str 字符串
     */
    public boolean isBlank(String str) {
        return !StringUtils.hasText(trim(str));
    }

    /**
     * 返回 trim 后的字符串，如果字符串参数为 null 也返回 null
     *
     * @param str 字符串
     * @return 两端无空白的字符串或 null
     */
    public String trim(String str) {
        if (str != null) {
            return str.trim();
        }
        return null;
    }

    /**
     * 返回删除所有空白字符后的字符串（包括有效字符中间的）
     *
     * @param str 字符串
     * @return 无空白字符的字符串
     */
    public String removeWhiteBlank(String str) {
        if (str != null) {
            return str.replaceAll("\\s", "");
        }
        return null;
    }

    /**
     * 删除数字值中可能的所有逗号（千分号）
     *
     * @param str 要清空逗号的原始字符串
     * @return 清空逗号号的字段串
     */
    private String removeThousandths(String str) {
        if (str != null) {
            return str.replaceAll(",", "");
        }
        return null;
    }

    /**
     * 删除数字值中可能存在的小数位
     *
     * @param str 要清 0 值的原始字符串
     * @return 没有小数位的字符串
     */
    private String removeDecimalPlaces(String str) {
        if (str != null && str.contains(".")) {
            return str.split("\\.")[0];
        }
        return str;
    }

    /**
     * 返回字符串表示的小数值，如果字符串为空白，返回 null
     *
     * @param str 字符串
     * @return 小数或 null
     * @throws NumberFormatException 字符串不是有效的小数格式时
     */
    public BigDecimal toDecimal(String str) {
        String s = trim(str);
        if (StringUtils.hasText(s)) {
            s = removeThousandths(s);
            return new BigDecimal(s);
        }
        return null;
    }

    /**
     * 返回字符串表示的小数值，如果字符串为空白或出错，返回 null
     *
     * @param str 字符串
     * @return 小数或 null
     */
    public BigDecimal tryToDecimal(String str) {
        try {
            return toDecimal(str);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 返回字符串表示的小数值，如果字符串为空白，返回 null
     *
     * @param str    字符串
     * @param prefix 可选前缀
     * @param suffix 可选后缀
     * @return 小数或 null
     * @throws NumberFormatException 字符串不是有效的小数格式时
     */
    public BigDecimal toDecimal(String str, String[] prefix, String[] suffix) {
        String s = trim(str);
        if (StringUtils.hasText(s)) {
            if (prefix != null) {
                for (String p : prefix) {
                    if (s.startsWith(p)) {
                        s = s.substring(p.length());
                        break;
                    }
                }
            }
            if (suffix != null) {
                for (String x : suffix) {
                    if (s.endsWith(x)) {
                        s = s.substring(0, s.length() - x.length());
                        break;
                    }
                }
            }
            s = removeThousandths(s);
            return new BigDecimal(s);
        }
        return null;
    }

    /**
     * 返回字符串表示的小数值，如果字符串为空白或出错，返回 null
     *
     * @param str    字符串
     * @param prefix 可选前缀
     * @param suffix 可选后缀
     * @return 小数或 null
     */
    public BigDecimal tryToDecimal(String str, String[] prefix, String[] suffix) {
        try {
            return toDecimal(str, prefix, suffix);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 返回字符串表示的整数值，如果字符串为空白，返回 null
     *
     * @param str 字符串
     * @return 整数或 null
     * @throws NumberFormatException 字符串不是有效的整数格式时
     */
    public Integer toInteger(String str) {
        String s = trim(str);
        if (StringUtils.hasText(s)) {
            s = removeThousandths(s);
            s = removeDecimalPlaces(s);
            return Integer.parseInt(s, 10);
        }
        return null;
    }

    /**
     * 返回字符串表示的整数值，如果字符串为空白或出错，返回 null
     *
     * @param str 字符串
     * @return 整数或 null
     */
    public Integer tryToInteger(String str) {
        try {
            return toInteger(str);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 返回字符串表示的长整数值，如果字符串为空白，返回 null
     *
     * @param str 字符串
     * @return 长整数或 null
     * @throws NumberFormatException 字符串不是有效的整数格式时
     */
    public Long toLong(String str) {
        String s = trim(str);
        if (StringUtils.hasText(s)) {
            s = removeThousandths(s);
            s = removeDecimalPlaces(s);
            return Long.parseLong(s);
        }
        return null;
    }

    /**
     * 返回字符串表示的长整数值，如果字符串为空白或出错，返回 null
     *
     * @param str 字符串
     * @return 长整数或 null
     */
    public Long tryToLong(String str) {
        try {
            return toLong(str);
        } catch (Exception e) {
            return null;
        }
    }

    private static final Pattern[] datePatterns = new Pattern[]{
            Pattern.compile("(\\d{2,4})年(\\d{1,2})月(\\d{1,2})日?"),
            Pattern.compile("(\\d{2,4})-(\\d{1,2})-(\\d{1,2})"),
            Pattern.compile("(\\d{4})/(\\d{1,2})/(\\d{1,2})")
    };

    /**
     * 一个小数，用到日期中是 1900 开始过去的天数
     */
    private static final Pattern dateDay1900Patterns = Pattern.compile("(\\d{1,6}\\.\\d*)|(\\d{1,5}\\d*)");

    private static final long MILLSECONDS_PRE_DAY = 24 * 60 * 60 * 1000;

    /**
     * 如果给定的 str 是特殊的需要格式化才能正常预览的日期值，调用格式化回调，否则忽略
     *
     * @param str              输入的日期
     * @param reformatConsumer 当预览需要格式化时的回调
     */
    public void datePreviewFormat(String str, Consumer<String> reformatConsumer) {
        String s = removeWhiteBlank(str);
        if (StringUtils.hasText(s)) {
            Matcher matcher = null;
            matcher = dateDay1900Patterns.matcher(s);
            if (matcher.matches()) {
                BigDecimal days = toDecimal(s);
                Calendar calendar = Calendar.getInstance();
                calendar.set(1900, Calendar.JANUARY, 0, 0, 0, 0);
                calendar.set(Calendar.DATE, days.intValue() - 1);
                Date date = calendar.getTime();
                reformatConsumer.accept(DateUtils.toString(date, DateUtils.PATTERN_DATE));
            }
        }
    }

    /**
     * 返回字符串表示的日期值（无时间、多格式兼容）如字段串为空白，返回 null
     *
     * @param str 字符串，可以是“yyyy年m月d日”、“yy年m月d日”、“yyyy年m月d”、“yy年m月d”、“yy-m-d”、“yyyy-m-d”或“yyyy/m/d”
     * @return
     */
    public Date toDate(String str) {
        String s = removeWhiteBlank(str);
        if (StringUtils.hasText(s)) {
            Matcher matcher = null;
            matcher = dateDay1900Patterns.matcher(s);
            if (matcher.matches()) {
                BigDecimal days = toDecimal(s);
                Calendar calendar = Calendar.getInstance();
                calendar.set(1900, Calendar.JANUARY, 0, 0, 0, 0);
                calendar.set(Calendar.DATE, days.intValue() - 1);
                return calendar.getTime();
            }
            for (int i = 0; i < datePatterns.length; i++) {
                matcher = datePatterns[i].matcher(s);
                if (matcher.matches()) {
                    break;
                }
            }
            // yyyy年m月d日
            if (matcher.matches()) {
                return toDate(matcher.group(1), matcher.group(2), matcher.group(3));
            }
            throw new DateTimeParseException(MessageUtils.getOrDefault(MessageKeys.EXCEPT_NOT_DATE_FORMAT, "不是有效的日期格式"), str, 0);
        }
        return null;
    }

    /**
     * 返回年月日字符串组合的日期值
     *
     * @param year  年，四位或两位
     * @param month 月，一位或两位
     * @param day   日，一位或两位
     * @return
     * @throws DateTimeParseException 格式不正确
     */
    public Date toDate(String year, String month, String day) {
        Integer iYear = toInteger(year);
        Integer iMonth = toInteger(month);
        Integer iDay = toInteger(day);
        if (iYear < 100) {
            // 两位年按 50 为中间分隔提升到四位年
            iYear = iYear > 50 ? (iYear + 1900) : (iYear + 2000);
        }
        if (iMonth < 1 || iMonth > 12) {
            throw new DateTimeParseException(MessageUtils.getOrDefault(MessageKeys.EXCEPT_DATE_MONTH_OUT_RANGE, "月份值不在有效范围"), month, 0);
        }
        if (iDay < 1 || iDay > 31) {
            throw new DateTimeParseException(MessageUtils.getOrDefault(MessageKeys.EXCEPT_DATE_DAY_OUT_RANGE, "日期值不在有效范围"), day, 0);
        }

        Calendar calendar = Calendar.getInstance();
        calendar.set(iYear, iMonth - 1, iDay, 0, 0, 0);
        return calendar.getTime();
    }

    public Date tryToDate(String str) {
        try {
            return toDate(str);
        } catch (Exception e) {
            return null;
        }
    }

    private static final Pattern[] timePatterns = new Pattern[]{
            Pattern.compile("(\\d{1,2})点(\\d{1,2})分(\\d{1,2})秒?"),
            Pattern.compile("(\\d{1,2})点(\\d{1,2})分?"),
            Pattern.compile("(\\d{1,2})点"),
            Pattern.compile("(\\d{1,2}):(\\d{1,2}):(\\d{1,2})"),
            Pattern.compile("(\\d{1,2}):(\\d{1,2})")
    };

    /**
     * 返回字符串表示的日期时间值（多格式兼容）如字段串为空白，返回 null
     *
     * @param str 字符串，日期部分可以是“yyyy年m月d日”、“yy年m月d日”、“yy-m-d”、“yyyy-m-d”或“yyyy/m/d”，时间部分可以是“H点M分”，“H点M分S秒”, “HH:MM”, “HH:MM:ss”，日期和时间之间可包括空格
     * @return
     */
    public Date toDateTime(String str) {
        String s = removeWhiteBlank(str);
        if (StringUtils.hasText(s)) {
            // 先处理时间部分
            String hourOfDay = null;
            String minus = "0";
            String second = "0";
            Matcher timeMatcher = null;
            for (int i = 0; i < timePatterns.length; i++) {
                timeMatcher = timePatterns[i].matcher(str);
                if (timeMatcher.find(0)) {
                    break;
                }
            }
            if (!timeMatcher.find(0)) {
                throw new DateTimeParseException(MessageUtils.getOrDefault(MessageKeys.EXCEPT_NOT_DATETIME_FORMAT, "日期时间格式错误"), str, 0);
            }

            hourOfDay = timeMatcher.group(1);
            if (timeMatcher.groupCount() > 2) {
                minus = timeMatcher.group(2);
            }
            if (timeMatcher.groupCount() > 3) {
                second = timeMatcher.group(3);
            }
            // 处理日期部分
            String dateStr = str.substring(0, timeMatcher.start());
            Date dateOnly = toDate(dateStr);

            // 合成日期时间结果
            Calendar calender = Calendar.getInstance();
            calender.setTime(dateOnly);
            return toDateTime(
                    toString(calender.get(Calendar.YEAR)),
                    toString(calender.get(Calendar.MONTH) + 1),
                    toString(calender.get(Calendar.DAY_OF_MONTH)),
                    hourOfDay,
                    minus,
                    second
            );
        }
        return null;
    }

    /**
     * 返回年月日字符串组合的日期时间值
     *
     * @param year      年，四位或两位
     * @param month     月，一位或两位
     * @param day       日，一位或两位
     * @param hourOfDay 小时，一位或两位
     * @param minute    分，一位或两位
     * @param second    秒，一位或两位
     * @return
     * @throws DateTimeParseException 格式不正确
     */
    public Date toDateTime(String year, String month, String day, String hourOfDay, String minute, String second) {
        Integer iYear = toInteger(year);
        Integer iMonth = toInteger(month);
        Integer iDay = toInteger(day);
        Integer iHourOfDay = toInteger(hourOfDay);
        Integer iMinute = toInteger(minute);
        Integer iSecond = toInteger(second);
        if (iYear < 100) {
            // 两位年按 50 为中间分隔提升到四位年
            iYear = iYear > 50 ? (iYear + 1900) : (iYear + 2000);
        }
        if (iMonth < 1 || iMonth > 12) {
            throw new DateTimeParseException(MessageUtils.getOrDefault(MessageKeys.EXCEPT_DATE_MONTH_OUT_RANGE, "月份值不在有效范围"), month, 0);
        }
        if (iDay < 1 || iDay > 31) {
            throw new DateTimeParseException(MessageUtils.getOrDefault(MessageKeys.EXCEPT_DATE_DAY_OUT_RANGE, "日期值不在有效范围"), day, 0);
        }
        if (iHourOfDay < 0 || iHourOfDay > 23) {
            throw new DateTimeParseException(MessageUtils.getOrDefault(MessageKeys.EXCEPT_TIME_HOUR_OUT_RANGE, "小时值不在有效范围"), hourOfDay, 0);
        }
        if (iMinute < 0 || iMinute > 59) {
            throw new DateTimeParseException(MessageUtils.getOrDefault(MessageKeys.EXCEPT_TIME_MINUS_OUT_RANGE, "分钟值不在有效范围"), minute, 0);
        }
        if (iSecond < 0 || iSecond > 59) {
            throw new DateTimeParseException(MessageUtils.getOrDefault(MessageKeys.EXCEPT_TIME_SECOND_OUT_RANGE, "秒值不在有效范围"), second, 0);
        }

        Calendar calendar = Calendar.getInstance();
        calendar.set(iYear, iMonth - 1, iDay, iHourOfDay, iMinute, iSecond);
        return calendar.getTime();
    }

    /**
     * 返回字符串表示的日期时间值（多格式兼容）如字段串为空白或出错，返回 null
     *
     * @param str 字符串
     * @return
     */
    public Date tryToDateTime(String str) {
        try {
            return toDateTime(str);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 返回字符串表示的布尔值，如字段串为空白，返回 null
     *
     * @param str 字符串
     * @return
     */
    public Boolean toBoolean(String str) {
        String s = trim(str);
        if (StringUtils.hasText(s)) {
            if ("是".equals(s) || "真".equals(s) || "true".equalsIgnoreCase(s) || "t".equalsIgnoreCase(s) || "yes".equalsIgnoreCase(s) || "y".equalsIgnoreCase(s)) {
                return true;
            }
            if ("否".equals(s) || "假".equals(s) || "false".equalsIgnoreCase(s) || "f".equalsIgnoreCase(s) || "no".equalsIgnoreCase(s) || "n".equalsIgnoreCase(s)) {
                return false;
            }
            throw new RuntimeException(MessageUtils.getOrDefault(MessageKeys.EXCEPT_NOT_BOOLEAN_FORMAT, "不是有效的布尔值"));
        }
        return null;
    }

    /**
     * 返回字符串表示的布尔值，如字段串为空白或出错，返回 null
     *
     * @param str 字符串
     * @return
     */
    public Boolean tryToBoolean(String str) {
        try {
            return toBoolean(str);
        } catch (Exception e) {
            return null;
        }
    }

    private static final Pattern SUFFIX_PATTERN = Pattern.compile(".*\\(([^(]+)\\)$");

    /**
     * 如果输入有后缀，提取后缀内容，否则返回原字符串
     * @param str 字符串
     * @return
     */
    public String tryGetImportSuffix(String str) {
        if (StringUtils.hasText(str)) {
            Matcher matcher = SUFFIX_PATTERN.matcher(str);
            if (matcher.matches()) {
                return matcher.group(1);
            }
        }
        return str;
    }


    private ExecuteScriptEngine engine;

    public ExecuteScriptEngine getEngine() {
        if (engine == null) {
            engine = new ExecuteScriptEngine();
        }
        return engine;
    }


    /**
     * Excel关联导入处理
     *
     * @param parentRow
     * @param row
     * @param parentVo
     * @param vo
     * @param matcheItem
     * @param joinModels
     * @param <ParentRow>
     * @param <Row>
     * @param <ParentVo>
     * @param <Vo>
     * @param <MatcheItem>
     */
    @SneakyThrows
    public <ParentRow, Row, ParentVo, Vo, MatcheItem> void joinHandle(ImportRow<ParentRow> parentRow, ImportRow<Row> row, ParentVo parentVo, Vo vo, MatcheItem matcheItem, List<JoinModel> joinModels) {
        if (ObjectUtil.isEmpty(row) || CollUtil.isEmpty(joinModels) || ObjectUtil.isEmpty(matcheItem)) {
            return;
        }
        for (JoinModel item : joinModels) {
            Object fieldValue = ReflectUtil.getFieldValue(vo, item.getTargetFieldName());
            if(ObjUtil.isEmpty(item.getImportIgnore())){
                item.setImportIgnore(false);
            }
            if (item.getImportIgnore()){
                continue;
            }
//            if (ObjectUtil.isNotEmpty(fieldValue)) {
//                continue;
//            }
            JoinResult result = null;
            if (StrUtil.equals("field", item.getSourceType())) {
                result = field(matcheItem, item.getSourceFieldName());
            } else if (StrUtil.equals("constant", item.getSourceType())) {
                result = constantField(item.getSourceConstant());
            } else if (StrUtil.equals("api", item.getSourceType())) {
                String sourceApi = StrUtil.concat(true, "http://", InetAddress.getLocalHost().getHostAddress(), ":", SpringUtil.getProperty("spring.servlet.port"), "/api", item.getSourceApi());
                result = apiField(parentVo, vo, item.getApiParams(), sourceApi);
            } else if (StrUtil.equals("expression", item.getSourceType())) {
                result = calculateField(vo, item.getSourceExpression());
            }
            //成功回写
            if (ObjectUtil.isNotEmpty(result) && result.isSuccess()) {
                ReflectUtil.setFieldValue(vo, item.getTargetFieldName(), result.getVal());
                ReflectUtil.setFieldValue(row.getRow(), item.getRowFieldName(), result.getVal());
                ReflectUtil.setFieldValue(row.getRow(), item.getTargetFieldName(), result.getVal());
            } else {
                ReflectUtil.setFieldValue(vo, item.getTargetFieldName(), null);
                if (item.getRequired()) {
                    row.addError(item.getRowFieldName(), StrUtil.concat(true, "处理字段错误，信息：", Convert.toStr(result.getVal())));
                }
            }
        }
    }

    /**
     * JOIN接口列处理
     *
     * @param parentVo
     * @param vo
     * @param apiParams
     * @param sourceApi
     * @return
     */
    private <ParentVo, Vo> JoinResult apiField(ParentVo parentVo, Vo vo, Map apiParams, String sourceApi) {
        sourceApi = StrUtil.replace(sourceApi, Pattern.compile("\\$\\{context\\.([^}]+)\\}"), m -> Convert.toStr(ReflectUtil.getFieldValue(vo, m.group(1)), ""));
        sourceApi = StrUtil.replace(sourceApi, Pattern.compile("\\$\\{parentContext\\.([^}]+)\\}"), m -> ObjectUtil.isEmpty(parentVo) ? StrUtil.EMPTY : Convert.toStr(ReflectUtil.getFieldValue(parentVo, m.group(1)), ""));
        HttpHeaders headers = new HttpHeaders();
        headers.add(JwtUserInfo.HeaderName, JwtUserInfo.getToken());
        try {
            Object o = getEngine().httpRequest(sourceApi, HttpMethod.POST, apiParams, headers);
            Object data = JSONPath.eval(o, "['data']", true);
            if (ObjectUtil.isEmpty(data)) {
                return JoinResult.fail(Convert.toStr(JSONPath.eval(o, "['errorMessage']", true)));
            } else {
                Object val = JSONPath.eval(data, "['value']", true);
                if (ObjectUtil.isNotEmpty(val)) {
                    return JoinResult.success(val);
                } else {
                    return JoinResult.success(data);
                }
            }
        } catch (Exception ex) {
            return JoinResult.fail(StrUtil.subPre(ExceptionUtil.getRootCauseMessage(ex), 2000));
        }
    }

    /**
     * JOIN固定列处理
     *
     * @param val
     * @return
     */
    private JoinResult constantField(String val) {
        try {
            return JoinResult.success(val);
        } catch (Exception ex) {
            return JoinResult.fail(StrUtil.subPre(ExceptionUtil.getRootCauseMessage(ex), 2000));
        }
    }


    /**
     * JOIN字段列处理
     *
     * @param matcheItem
     * @param sourceFieldName
     * @param <MatcheItem>
     * @return
     */
    private <MatcheItem> JoinResult field(MatcheItem matcheItem, String sourceFieldName) {
        try {
            Object val = ReflectUtil.getFieldValue(matcheItem, sourceFieldName);
            return JoinResult.success(val);
        } catch (Exception ex) {
            return JoinResult.fail(StrUtil.subPre(ExceptionUtil.getRootCauseMessage(ex), 2000));
        }
    }

    /**
     * JOIN计算列处理
     *
     * @param vo
     * @param expression
     * @param <Vo>
     * @return
     */
    private <Vo> JoinResult calculateField(Vo vo, String expression) {
        try {
            String calculate = ReUtil.replaceAll(expression, Pattern.compile("getValue\\((.+?)\\)"),
                    m -> Convert.toStr(ReflectUtil.getFieldValue(vo, ReUtil.getGroup0("(?<=\\('|\")(.+?)(?=\\\\|\\'|\"\\))", m.group(0)))));
            String val = NumberUtil.decimalFormatMoney(NumberUtil.calculate(calculate));
            return JoinResult.success(val);
        } catch (Exception ex) {
            return JoinResult.fail(StrUtil.subPre(ExceptionUtil.getRootCauseMessage(ex), 2000));
        }
    }

    /**
     * 计算列处理
     *
     * @param vo
     * @param expression
     * @param fieldName
     * @param <Vo>
     */
    public <Row, Vo> void calculateField(Row row, Vo vo, String expression, String fieldName) {
        try {
            JoinResult res = calculateField(vo, expression);
            if (res.isSuccess()) {
                ReflectUtil.setFieldValue(row, fieldName, res.getVal());
                ReflectUtil.setFieldValue(vo, fieldName, res.getVal());
            }
        } catch (Exception ex) {
            ReflectUtil.setFieldValue(vo, fieldName, null);
        }
    }


    /**
     * 设置模版名称到表
     *
     * @param vo
     * @param sysImportTemplateName
     * @param <Vo>
     */
    public <Vo> void setSysImportTemplateName(Vo vo, String sysImportTemplateName) {
        String defaultFieldName = "sysImportTemplateName";
        try {
            if (ReflectUtil.hasField(ClassUtil.getClass(vo), defaultFieldName)) {
                ReflectUtil.setFieldValue(vo, defaultFieldName, sysImportTemplateName);
            }
        } catch (Exception ex) {
        }
    }

    /**
     * 设置模版批次号到表
     *
     * @param vo
     * @param sysImportBatchNo
     * @param <Vo>
     */
    public <Vo> void setSysImportBatchNo(Vo vo, String sysImportBatchNo) {
        String defaultFieldName = "sysImportBatchNo";
        try {
            if (ReflectUtil.hasField(ClassUtil.getClass(vo), defaultFieldName)) {
                ReflectUtil.setFieldValue(vo, defaultFieldName, sysImportBatchNo);
            }
        } catch (Exception ex) {
        }
    }

    /**
     * Excel关联导入处理（废弃）
     *
     * @param parentRow
     * @param row
     * @param parentVo
     * @param vo
     * @param matcheItem
     * @param returnMaps
     * @param returnApiRequestParams
     * @param <ParentRow>
     * @param <Row>
     * @param <ParentVo>
     * @param <Vo>
     * @param <MatcheItem>
     * @return
     */
    @SneakyThrows
    public <ParentRow, Row, ParentVo, Vo, MatcheItem> List<Map> handleReturnMaps(ImportRow<ParentRow> parentRow, ImportRow<Row> row, ParentVo parentVo, Vo vo, MatcheItem matcheItem, List<Map> returnMaps, Map returnApiRequestParams) {
        if (ObjectUtil.isEmpty(row) || CollUtil.isEmpty(returnMaps) || ObjectUtil.isEmpty(matcheItem) || MapUtil.isEmpty(returnApiRequestParams)) {
            return returnMaps;
        }
        for (Map map : returnMaps) {
            String sourceType = MapUtil.getStr(map, "sourceType");
            String targetFieldName = MapUtil.getStr(map, "targetFieldName");
            String rowFieldName = MapUtil.getStr(map, "rowFieldName");
            //导入的vo包含该数据:不处理
            Object fieldValue = ReflectUtil.getFieldValue(vo, targetFieldName);
            if (ObjectUtil.isNotEmpty(fieldValue)) {
                continue;
            }
            if (StrUtil.equals("field", sourceType)) {
                String sourceFieldName = MapUtil.getStr(map, "sourceFieldName");
                Object val = ReflectUtil.getFieldValue(matcheItem, sourceFieldName);
                ReflectUtil.setFieldValue(vo, targetFieldName, val);
                map.put("rowFieldValue", val);
            } else if (StrUtil.equals("constant", sourceType)) {
                String val = MapUtil.getStr(map, "sourceConstant");
                ReflectUtil.setFieldValue(vo, targetFieldName, val);
                map.put("rowFieldValue", val);
            } else if (StrUtil.equals("api", sourceType)) {
                String sourceApi = StrUtil.concat(true, "http://", InetAddress.getLocalHost().getHostAddress(), ":", SpringUtil.getProperty("spring.servlet.port"), "/api", MapUtil.getStr(map, "sourceApi"));
                sourceApi = StrUtil.replace(sourceApi, Pattern.compile("\\$\\{context\\.([^}]+)\\}"), m -> Convert.toStr(ReflectUtil.getFieldValue(vo, m.group(1)), ""));
                sourceApi = StrUtil.replace(sourceApi, Pattern.compile("\\$\\{parentContext\\.([^}]+)\\}"), m -> ObjectUtil.isEmpty(parentVo) ? StrUtil.EMPTY : Convert.toStr(ReflectUtil.getFieldValue(parentVo, m.group(1)), ""));
                HttpHeaders headers = new HttpHeaders();
                headers.add(JwtUserInfo.HeaderName, JwtUserInfo.getToken());
                try {
                    Object o = getEngine().httpRequest(sourceApi, HttpMethod.POST, returnApiRequestParams, headers);
                    Object data = JSONPath.eval(o, "['data']", true);
                    if (ObjectUtil.isEmpty(data)) {
                        String val = Convert.toStr(JSONPath.eval(o, "['errorMessage']", true));
                        //row.addError(rowFieldName, val);
                    } else {
                        Object val = JSONPath.eval(data, "['value']", true);
                        if (ObjectUtil.isNotEmpty(val)) {
                            ReflectUtil.setFieldValue(vo, targetFieldName, val);
                            map.put("rowFieldValue", val);
                        } else {
                            ReflectUtil.setFieldValue(vo, targetFieldName, data);
                            map.put("rowFieldValue", data);
                        }
                    }
                } catch (Exception ex) {
                    String val = StrUtil.concat(true, "处理 api 返回失败！", ExceptionUtil.getRootCauseMessage(ex));
                    //row.addError(rowFieldName, val);
                }
            } else if (StrUtil.equals("expression", sourceType)) {
                String sourceExpression = MapUtil.getStr(map, "sourceExpression");
                try {
                    String val = StrUtil.concat(true, "暂不支持表达式类型！");
                    //row.addError(rowFieldName, val);
                } catch (Exception ex) {
                    String val = StrUtil.concat(true, "处理 表达式 返回失败！", ExceptionUtil.getRootCauseMessage(ex));
                    //row.addError(rowFieldName, val);
                }
            }
        }
        return returnMaps;
    }
}
